package utils

import (
	"errors"
	"fmt"
	"log"
	"runtime"
	"sync"
)

const (
	panicStacktraceBufferSize = 1 << 10
)

func Go(fns ...func(chan error)) <-chan error {
	var wg = new(sync.WaitGroup)
	// 如果有多个调用失败时，unbuffered channel将会产生有的goroutine永远无法返回
	// 所以这里我们make的时候必须带上len(fns)
	var done = make(chan error, len(fns))

	wg.Add(len(fns))
	for _, fn := range fns {
		go func(f func(chan error)) {
			defer func() {
				if v := recover(); v != nil {
					done <- errors.New(fmt.Sprint(v))
					buf := make([]byte, panicStacktraceBufferSize)
					buf = buf[:runtime.Stack(buf, false)]
					log.Printf("panic recover: %v\n%s", v, buf)
				}
				wg.Done()
			}()
			f(done)
		}(fn)
	}
	go func() { wg.Wait(); done <- nil }()
	return done
}
